package top.codingfly.netty.server;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.handler.codec.MessageToByteEncoder;
import io.netty.handler.logging.LogLevel;
import io.netty.handler.logging.LoggingHandler;
import io.netty.handler.timeout.IdleState;
import io.netty.handler.timeout.IdleStateEvent;
import io.netty.handler.timeout.IdleStateHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import top.codingfly.netty.common.*;

import java.util.List;
import java.util.concurrent.TimeUnit;

public class NettyServer {

    public Integer port;
    public LogLevel logLevel;
    public NioEventLoopGroup bossGroup;
    public NioEventLoopGroup workerGroup;
    public NettyHandler nettyHandler;
    public Integer heartbeatTimeout = 30; // 心跳时间默认30秒
    private final static Logger logger = LoggerFactory.getLogger(NettyServer.class);

    /**
     * Boss 线程组
     *     职责：负责处理客户端的连接请求。
     *     线程数：通常为 1 或多个，具体根据需求配置。
     * Worker 线程组
     *     职责：负责处理 I/O 读写操作。
     *     线程数：通常是 CPU 核数的两倍或更多，具体根据需求配置。
     * @param nettyHandler
     * @param logLevel
     */
    public NettyServer(NettyHandler nettyHandler, LogLevel logLevel) {
        if (nettyHandler ==null) {
            throw new RuntimeException("nettyHandler not allow null");
        }
        this.logLevel = logLevel;
        this.nettyHandler = nettyHandler;
        int bossThreadCount = 2;
        int WorkerThreadCount = 5;
        this.bossGroup = new NioEventLoopGroup(bossThreadCount);
        this.workerGroup = new NioEventLoopGroup(WorkerThreadCount);
    }

    /**
     * Boss 线程组
     *     职责：负责处理客户端的连接请求。
     *     线程数：通常为 1 或多个，具体根据需求配置。
     * Worker 线程组
     *     职责：负责处理 I/O 读写操作。
     *     线程数：通常是 CPU 核数的两倍或更多，具体根据需求配置。
     * @param nettyHandler
     * @param logLevel
     */
    public NettyServer(NettyHandler nettyHandler, int bossThreadCount, int WorkerThreadCount, LogLevel logLevel) {
        if (nettyHandler ==null) {
            throw new RuntimeException("nettyHandler not allow null");
        }
        this.logLevel = logLevel;
        this.nettyHandler = nettyHandler;
        this.bossGroup = new NioEventLoopGroup(bossThreadCount);
        this.workerGroup = new NioEventLoopGroup(WorkerThreadCount);
    }

    public void start(Integer port) {
        this.port = port;
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap
                .group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>(){
                    @Override
                    protected void initChannel(SocketChannel socketChannel) throws Exception {
                        ChannelPipeline pipeline = socketChannel.pipeline();
                        if (logLevel!=null) { // 无需打印netty框架的日志, netty框架几乎很少bug，即使有，自己没能力改netty的源码
//                            pipeline.addLast(new LoggingHandler(logLevel));
                        }
                        pipeline.addLast(nettyDecoder()); // 每次都要创建新的对象
                        pipeline.addLast(createHandler());// 每次都要创建新的对象
                        pipeline.addLast(nettyEncoder()); // 每次都要创建新的对象
                        pipeline.addLast(new ServerHeartbeatHandler(heartbeatTimeout));
                    }
                });
        try {
            ChannelFuture channelFuture = serverBootstrap.bind(port).sync(); // 绑定端口
            logger.info("Netty Server port:"+this.port);
            channelFuture.channel().closeFuture().sync(); // 等待直到服务器socket关闭
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            logger.info("Netty Server port:"+this.port+" error:"+e.getMessage());
        }
    }

    public void close() {
        try {
            bossGroup.shutdownGracefully();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
        try {
            workerGroup.shutdownGracefully();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
        }
    }

    // 发消息, 要知道发给谁
    public void send(ChannelHandlerContext ctx, NettyPacket msg) {
        ctx.channel().writeAndFlush(msg);
    }

    private class ServerHeartbeatHandler extends IdleStateHandler {

        // 设置写事件为5s
        // 如果5s没有写事件发生 就会触发下面的IdleStateEvent
        // private static final int WRITE_IDLE_TIME = 5;
        private Integer READER_IDLE_TIME;

        public ServerHeartbeatHandler(int heartbeatTimeout) {
            super(heartbeatTimeout, 5, 5, TimeUnit.SECONDS);
            this.READER_IDLE_TIME = heartbeatTimeout;
        }

        @Override
        protected void channelIdle(ChannelHandlerContext ctx, IdleStateEvent evt) throws Exception {
            // 指定时间内没有读事件发送 就会触发 IdleState.READER_IDLE 类型事件
            // 我们就可以对该连接进行处理 这里是直接关闭
            if(evt.state() == IdleState.READER_IDLE) {
                logger.info(READER_IDLE_TIME+" 秒内没有读取到 "+ctx.channel()+" 数据, 关闭连接");
                ctx.channel().close();
            }
        }
    }

    private ByteToMessageDecoder nettyDecoder() {
        return new ByteToMessageDecoder() {
            @Override
            protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
                nettyHandler.decode(ctx, in, out);
            }
        };
    }

    private MessageToByteEncoder nettyEncoder() {
        return new MessageToByteEncoder() {
            @Override
            protected void encode(ChannelHandlerContext ctx, Object msg, ByteBuf out) throws Exception {
                nettyHandler.encode(ctx, msg, out);
            }
        };
    }

    private ChannelInboundHandlerAdapter createHandler() {
        return new ChannelInboundHandlerAdapter() {

            @Override
            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                nettyHandler.handle(ctx, msg);
            }

            @Override
            public void channelRegistered(ChannelHandlerContext ctx) throws Exception {
                nettyHandler.channelRegistered(ctx);
            }

            @Override
            public void channelUnregistered(ChannelHandlerContext ctx) throws Exception {
                nettyHandler.channelUnregistered(ctx);
            }

            @Override
            public void channelActive(ChannelHandlerContext ctx) throws Exception {
                nettyHandler.channelActive(ctx);
            }

            @Override
            public void channelInactive(ChannelHandlerContext ctx) throws Exception {
                nettyHandler.channelInactive(ctx);
            }

            @Override
            public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
                nettyHandler.exceptionCaught(ctx, cause);
            }
        };
    }

}
